
#include "aprtools/APRAutoMemoryPool.h"
#include "net/Broker5Client.h"
#include "iface/testerror.h"

#include "acquire_common.h"
#include "acquire_common_private.h"

// Return value doesn't matter, if these fail bad things happen anyway

/** Imports a key from a file into the database. These are intended
 * for the keys written securely to the compute.
 * 
 * @param keydb The key database to add the keys to.
 * 
 * @param keyrealtype The key algorithm and type, for instance
 * 'PublicRSA'.
 * 
 * @param keyname The key of the key when it is inserted into the
 * appropriate place in the database.
 * 
 * @param filename The filename to read the key data from.
 * 
 * @param t_mp The memory pool to use.
 * 
 * @param docommit Whether to commit() (insert) or update()
 * (update). If the information in the database already exists,
 * commit() fails, if it does not update() fails. */
void importKeyToDatabase(KeyDB *keydb, const char *keyrealtype, const char *keyname, const char *filename, apr_pool_t *t_mp, int docommit)
{
   Key::keypurposetype keytype;
   const KeyTableView *ktable = NULL;
   if (strstr(filename, ".enc"))
   {
      ktable = keydb->encryptionTable();
      keytype = Key::KEYPURPOSE_ENCRYPTION;
   }
   if (strstr(filename, ".dec"))
   {
      ktable = keydb->encryptionTable();
      keytype = Key::KEYPURPOSE_DECRYPTION;
   }
   if (strstr(filename, ".sgn"))
   {
      ktable = keydb->encryptionTable();
      keytype = Key::KEYPURPOSE_SIGNING;
   }
   if (strstr(filename, ".chk"))
   {
      ktable = keydb->encryptionTable();
      keytype = Key::KEYPURPOSE_CHECKING;
   }
   char *keydata = NULL;
   int retval = StringUtils::readFileToString(&keydata, filename, t_mp);
   Key::keyopenerror terr;
   AbstractKey *a_newkey = Key::tryToGetKeyFromText(keyrealtype, NULL, keydata, keyname, &terr);
   Key::showKeyOpenError(keyname, keytype, terr);
   if ((a_newkey) && (terr == 0))
   {
      Key tnewkey(keydb, keyname, keytype);
      tnewkey.initFromAbstractKey(keydb, *a_newkey, NULL, keytype);
      if (docommit)
      {
         tnewkey.commit();
      } else
      {
         tnewkey.update();
      }
   } else
   {
      errprint("an error occurred getting the key from the text (error %d)", terr);
   }
}

/** A function for managing the network exchange on the client
 * side. This usually involves exchanging a message asking something
 * of the broker, for a message containing a status or error code.
 * 
 * @param bmsg The broker message that is to be sent to the broker,
 * and subsequently replaced by a reply.
 * 
 * @param imp The memory pool to use for the exchange.
 * 
 * @param cancelflag A pointer to a piece of memory that is dirty-read
 * to provide an instant cancel to an external managing thread.
 * 
 * @return One if the exchange was cancelled or otherwise cut short,
 * zero on success. */
int exchangeMessage(BrokerMessage &bmsg, apr_pool_t *imp, int *cancelflag)
{
   return Broker5Client::exchangeMessage(bmsg, imp, cancelflag, state.hostname, state.portno, *(state.keydb), ACQUIRE_CLIENT_MAX_WAIT);
}

/** A function for modifying block counts (progress bars) on accessible
 * actors.
 * 
 * @param modifier_done Takes any one of SERVER_DO_NOTHING,
 * SERVER_REPLACE_BLOCK_COUNT, SERVER_ADD_TO_BLOCK_COUNT,
 * SERVER_SUBTRACT_FROM_BLOCK_COUNT. This modifier modifies
 * what is to be done to the blocks done stored in the 
 * actor references by the remote ID.
 * 
 * @param delta_done The number of blocks that is to be
 * interpreted via the modifier into an action to be done
 * to the blocks done count.
 * 
 * @param modifier_total Takes any one of SERVER_DO_NOTHING,
 * SERVER_REPLACE_BLOCK_COUNT, SERVER_ADD_TO_BLOCK_COUNT,
 * SERVER_SUBTRACT_FROM_BLOCK_COUNT. This modifier modifies
 * what is to be done to the blocks total stored in the 
 * actor references by the remote ID.
 * 
 * @param delta_total The number of blocks that is to be
 * interpreted via the modifier into an action to be done
 * to the blocks done count.
 * 
 * @param actorid The ID of the actor to be modified.
 * 
 * @return Zero on success, one on failure, minus one on
 * network issues. */
int acquireModifyBlocks(int modifier_done, int delta_done, int modifier_total, int delta_total, const char *actorid)
{
   FATAL_IF_BLANK(-1, actorid, "actor id name");
   APRAutoMemoryPool pool((state.apr_iface)->getGlobalPool());
   BrokerMessage bmsg(pool.mp());
   bmsg.setRecipient(state.operational_recipientkeyname);
   bmsg.setSender(state.operational_senderkeyname);
   if (((modifier_done == SERVER_DO_NOTHING) && (modifier_total == SERVER_DO_NOTHING)))
   {
      errprint("nothing to do to block numbers");
   } else
   {
      bmsg.addTypeHeaderNOCRLF("UPDATE_BLOCKS", actorid);
      switch (modifier_total)
      {
         case SERVER_REPLACE_BLOCK_COUNT:
            bmsg.addHeaderLineNOCRLF("Update-Total-Blocks: %d", delta_total);
            break;
         case SERVER_ADD_TO_BLOCK_COUNT:
            bmsg.addHeaderLineNOCRLF("Add-Total-Blocks: %d", delta_total);
            break;
         case SERVER_SUBTRACT_FROM_BLOCK_COUNT:
            bmsg.addHeaderLineNOCRLF("Subtract-Total-Blocks: %d", delta_total);
            break;
         default: break;
      }
      switch (modifier_done)
      {
         case SERVER_REPLACE_BLOCK_COUNT:
            bmsg.addHeaderLineNOCRLF("Update-Done-Blocks: %d", delta_done);
            break;
         case SERVER_ADD_TO_BLOCK_COUNT:
            bmsg.addHeaderLineNOCRLF("Add-Done-Blocks: %d", delta_done);
            break;
         case SERVER_SUBTRACT_FROM_BLOCK_COUNT:
            bmsg.addHeaderLineNOCRLF("Subtract-Done-Blocks: %d", delta_done);
            break;
         default: break;
      }
      if (exchangeMessage(bmsg, pool.mp(), NULL) == 1)
      {
         errprint("message exchange failed");
         return -1;
      }
      const APRTable *headertable = bmsg.getHeaderTable();
      if (bmsg.isProcessee("SUCCESS"))
      {
         return 0;
      } else
      return 1;
   }
   return 0;
}
